"""
Copyright 2012 Wieser Dietmar, Margit Mutschlechner.

This file is part of Multimedia Social Networking Platform.

Multimedia Social Networking Platform is free software: 
you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Multimedia Social Networking Platform is distributed
in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Multimedia Social Networking Platform.  
If not, see <http://www.gnu.org/licenses/>.

"""

from threading import Thread
from msnp.service import network
from msnp.logger import Logger
from msnp.service.network.sending import Sender, ReliableTransportQueue
from msnp.service.network.logic import ReliableTransportLogic
from msnp.service.network.model.ReliableTransportModel import RTFinishCrashMessage, \
    RTLeaveMessage, RTJoinMessage, RTSearchSuccessorMessage, RTRehelloMessage, \
    RTDeleteBackupMessage
from msnp.service.network.networktimer import ReliableTransportTimer
from msnp.service.network import LeaveRequest
import traceback


"""This module contains all functions needed for the reliable transport of messages.
- process such messages
- forwarding method (sequentially or by broadcast)
- threads to initiate a reliable transport message and to broadcast it."""


def processRTMessage(message, originalIdentity, alreadyReceivedUsers):
    """This function processes a received reliable transport Message.
    
    If this peer received the message already it means that the sequential forwarding was successful
    and the corresponding timer will be disabled.
    Otherwise it processes the message depending on its type.
    
    Args:
        message: the ReliableTransporMessage instance
        originalIdentity: the source identity (PeerInfo instance)
        alreadyReceivedUsers: a list of users (strings) that already received the message
    """
    
    network.NetworkLock.acquireRTLock()
    try:
        myIdentity = network.NetworkController.getMySelf().getMyIdentity()
        myName = myIdentity.getName()
        #if message passed through the ring
        if alreadyReceivedUsers != None and alreadyReceivedUsers.__contains__(myName):
            Logger.info("-------> RT-Message with type '" + message.getType() + "' and compareIdentity " + message.getCompareIdentity().getName() + " came through the ring without using broadcast")
            ReliableTransportTimer.disableRTTimerEntry(message)
            LeaveRequest.release()
        else:
            process = __processMessage(message, originalIdentity, alreadyReceivedUsers)
            # if i should process the message
            if process:
                Logger.info("processing RT message with type: " + message.getType())
                if isinstance(message, RTFinishCrashMessage):
                    ReliableTransportLogic.processFinishCrash(message)
                elif isinstance(message, RTLeaveMessage):
                    ReliableTransportLogic.processLeave(message)
                elif isinstance(message, RTJoinMessage):
                    ReliableTransportLogic.processJoin(message)
                elif isinstance(message, RTSearchSuccessorMessage):
                    ReliableTransportLogic.processSearchSuccessor(message)
                elif isinstance(message, RTRehelloMessage):
                    ReliableTransportLogic.processRehello(message)
                elif isinstance(message, RTDeleteBackupMessage):
                    ReliableTransportLogic.processDeleteBackup(message)
                else:
                    Logger.info("!!!! received RT-message with unknown message type " + message.getType() + "!!!!!!")
            else:
                Logger.info("don't process RT-message")
    except:
        Logger.info("error in processRTMessage")
        Logger.info(traceback.format_exc())
    finally:
        network.NetworkLock.releaseRTLock()
        
        
def __decideSequentiallyForwardingAndProcessing(message, originalIdentity, alreadyReceivedUsers):
    """This function decides whether the message will be forwarded sequentially or not.
    
    If the predecessor is None an error callback to the original sender is send because a broadcast is needed.
    If it was a search successor message and the predecessor is the crashed peer a reconnect ring message will be sent.
    Otherwise the message is forwarded sequentially.
    
    Args:
        message: the ReliableTransporMessage instance
        originalIdentity: the source identity (PeerInfo instance)
        alreadyReceivedUsers: a list of users (strings) that already received the message
    """
    
    predecessor = network.NetworkController.getMySelf().getPredecessor()
    if predecessor != None:
        myPredecessorName = predecessor.getName()
        myIdentity = network.NetworkController.getMySelf().getMyIdentity()
        # if mypredecessor is the crashed identity -> send reconnect
        # or mypredecessor is the sender of the message -> send reconnect (can happen when the sender has a wrong successor)
        if isinstance(message, RTSearchSuccessorMessage) and (message.getCrashedIdentity().getName() == myPredecessorName or message.getPredecessorOfCrashedIdentity().getName() == myPredecessorName):
            Logger.info("Got RTSEARCH_SUCCESSOR for crashed identity: " + message.getCrashedIdentity().getName() + "Sending reconnectRing to " + originalIdentity.getName())
            ReliableTransportTimer.disableRTTimerEntry(message)
            Sender.sendReconnectRing(originalIdentity, myIdentity, message)
            return False
        else:
            Logger.info("forward rt-message sequentially")
            alreadyReceivedUsers.append(myIdentity.getName())
            #if i am the sender start the timer
            if myIdentity.getName() == originalIdentity.getName():
                ReliableTransportTimer.addAndStartRTTimerEntry(message)
            Sender.sequentialDelivery(message, originalIdentity, alreadyReceivedUsers)
    else:
        #if predecessor = None the original identity should broadcast the message
        Logger.info("send rt-message error callback because predecessor==None")
        Sender.sendRTMessageErrorCallback(originalIdentity, message)
    return True


def __processMessage(message, originalIdentity, alreadyReceivedUsers):
    """This function decides whether to process the received message or not.
    
    If the message has already been processed the function returns False.
    If it is a broadcast the broadcast thread will be started.
    Otherwise it calls the decision is forwarded to the __decideSequentiallyForwardingAndProcessing function.
    
    Args:
        message: the ReliableTransporMessage instance
        originalIdentity: the source identity (PeerInfo instance)
        alreadyReceivedUsers: a list of users (strings) that already received the message"""
        
    process = False
    if ReliableTransportQueue.addRTEntry(message):
        # if it is already a broadcast
        if message.isUseBroadcast() or originalIdentity == None or alreadyReceivedUsers == None:
            broadcastThread = BroadcastThread(message)
            broadcastThread.start() 
            Logger.info("  -> broadcast forwarded")
            process = True
        # if it has to be forwarded sequentially
        else:
            process = __decideSequentiallyForwardingAndProcessing(message, originalIdentity, alreadyReceivedUsers)
    else:
        Logger.info("message already processed")
    return process
    
    
def initiateRTMessage(message):
    """This function initiates a new reliable transport message by creating an instance of the InitiateRTMessageThread class.
    
    Args:
        message: the ReliableTransportMessage instance.
    """
    
    initiateThread = InitiateRTMessageThread(message)
    initiateThread.start() 
    
    
def alreadyProcessedCrashMessage(crashedName):
    """This function determines whether the crash message that corresponds to the given name was already processed
    by this peer.
    
    The call is forwarded to the alreadyProcessedCrashMessage of the ReliableTransportQueue module.
    
    Returns:
        True if it was already processed, False otherwise"""
        
    return ReliableTransportQueue.alreadyProcessedCrashMessage(crashedName)


#############################
## InitiateRTMessageThread ##
#############################
    
    
class InitiateRTMessageThread(Thread):
    """Class for initiating a reliable transport message.
    
    Arguments:
        __message: the ReliableTransportMessage instance.
    """
    
    def __init__(self, message):
        Thread.__init__(self)
        self.__message = message
        
    def run(self):
        """The run method of the thread.
        
        The call is forwarded to the processRTMessage function.
        """
        
        LeaveRequest.block()
        Logger.info("I initiate a rt-message of type: " + self.__message.getType())
        originalIdentity = network.NetworkController.getMySelf().getMyIdentity()
        alreadyReceivedUsers = []
        processRTMessage(self.__message, originalIdentity, alreadyReceivedUsers)
    
    
#####################
## BroadcastThread ##
#####################
    
class BroadcastThread(Thread):
    """Class for broadcasting a reliable transport message.
    
    Arguments:
        __message: the ReliableTransportMessage instance.
    """
    
    def __init__(self, message):
        Thread.__init__(self)
        self.__message = message
        
    def run(self):
        """The run method of the thread.
        
        The broadcast is started using the broadcastMessage function of the Sender module.
        """
        
        Sender.broadcastMessage(self.__message)
        Logger.info("broadcast-thread finished")

